#ifndef PREFIX_STREAMBUF_HPP_
#define PREFIX_STREAMBUF_HPP_

class prefix_streambuf : public std::streambuf
{
public:

    prefix_streambuf(std::streambuf *sbuf, int len)
        : m_sbuf(sbuf), m_len(len), m_read(0)
    {
        setbuf(NULL,0);
    }

protected:
    
    int underflow()
    {
        if (m_read >= m_len)
            return traits_type::eof();
        return m_sbuf->sgetc();
    }
    
    int uflow()
    {
        if (m_read >= m_len)
            return traits_type::eof();
        m_read += 1;
        return m_sbuf->snextc();
    }
    
    std::streampos seekoff( std::streamoff off, std::ios_base::seekdir way,
            std::ios_base::openmode which = std::ios_base::in | std::ios_base::out )
    {
        switch (way)
        {
            case std::ios_base::beg:
            {
                break;
            }
            
            case std::ios_base::cur:
            {
                off += m_read;
                break;
            }
            
            case std::ios_base::end:
            {
                off += m_len;
                break;
            }
        }
        
        if (off < 0 || off > m_len)
        {
            return -1;
        }
        
        pos_type pos = m_sbuf->pubseekpos(off, which);
        if (pos >= 0)
        {
            m_len = pos;
        }
        
        return pos;
    }

    std::streampos seekpos( std::streampos sp,
            std::ios_base::openmode which = std::ios_base::in | std::ios_base::out)
    {
        if (sp < 0 || sp > m_len)
        {
            return -1;
        }
        
        pos_type pos = m_sbuf->pubseekpos(sp, which);
        if (pos >= 0)
        {
            m_len = pos;
        }
        
        return pos;
    }

private:
    std::streambuf *m_sbuf;
    std::streamsize m_len;
    std::streampos m_read;
};


#endif /* PREFIX_STREAMBUF_HPP_ */
